import redis
from redis.connection import ConnectionPool
import logging
import threading
import time
from typing import Any, Optional, Union, List, Dict
import json

_logger = logging.getLogger(__name__)


class RedisConnectionPool:
    """Redis 连接池管理类"""
    _pools: Dict[str, ConnectionPool] = {}
    _lock = threading.Lock()

    @classmethod
    def get_pool(cls, host: str, port: int, db: int, password: Optional[str] = None, **kwargs) -> ConnectionPool:
        """
        获取或创建连接池

        Args:
            host: Redis 服务器地址
            port: Redis 服务器端口
            db: 数据库编号
            password: Redis 密码
            **kwargs: 其他连接参数
        """
        key = f"{host}:{port}:{db}"
        if key not in cls._pools:
            with cls._lock:
                if key not in cls._pools:
                    pool = ConnectionPool(
                        host=host,
                        port=port,
                        db=db,
                        password=password,
                        decode_responses=True,
                        **kwargs
                    )
                    cls._pools[key] = pool
                    _logger.info(f"Created new Redis connection pool for {key}")
        return cls._pools[key]

    @classmethod
    def clear_pools(cls):
        """清理所有连接池"""
        with cls._lock:
            for pool in cls._pools.values():
                pool.disconnect()
            cls._pools.clear()


class RedisClient:
    """Redis 客户端封装类"""

    def __init__(self,
                 host: str = 'localhost',
                 port: int = 6379,
                 db: int = 0,
                 password: Optional[str] = None,
                 decode_responses: bool = True,
                 socket_timeout: int = 5,
                 retry_on_timeout: bool = True,
                 max_retries: int = 3):
        """初始化 Redis 客户端"""
        self.pool = RedisConnectionPool.get_pool(
            host=host,
            port=port,
            db=db,
            password=password,
            socket_timeout=socket_timeout,
            retry_on_timeout=retry_on_timeout
        )
        self._redis_conn = None
        self._lock = threading.Lock()
        self._connected = False
        self.max_retries = max_retries

    @property
    def redis_conn(self) -> redis.Redis:
        """获取 Redis 连接"""
        if not self._connected:
            with self._lock:
                if not self._connected:
                    self.connect()
        if self._redis_conn is None:
            raise redis.ConnectionError("Redis connection is not established")
        return self._redis_conn

    def connect(self) -> None:
        """建立 Redis 连接"""
        try:
            self._redis_conn = redis.Redis(connection_pool=self.pool)
            self._redis_conn.ping()
            self._connected = True
            _logger.debug("Successfully connected to Redis")
        except redis.ConnectionError as e:
            _logger.error(f"Failed to connect to Redis: {e}")
            self._connected = False
            raise

    def _retry_operation(self, operation):
        """重试操作装饰器"""
        retries = 0
        while retries < self.max_retries:
            try:
                return operation()
            except redis.ConnectionError:
                _logger.warning(f"Connection lost, retry {retries + 1}/{self.max_retries}")
                self.connect()
                retries += 1
            except Exception as e:
                _logger.error(f"Redis operation error: {e}")
                return None
        return None

    def set_json(self, key: str, value: Any, **kwargs) -> bool:
        """
        存储 JSON 数据

        Args:
            key: 键名
            value: 要存储的数据(将被JSON序列化)
            **kwargs: 其他set参数(ex, nx, xx等)
        """
        try:
            json_value = json.dumps(value)
            return self.set(key, json_value, **kwargs)
        except (TypeError, json.JSONDecodeError) as e:
            _logger.error(f"JSON serialization error: {e}")
            return False

    def get_json(self, key: str) -> Optional[Any]:
        """
        获取 JSON 数据

        Args:
            key: 键名
        """
        value = self.get(key)
        if value:
            try:
                return json.loads(value)
            except json.JSONDecodeError as e:
                _logger.error(f"JSON deserialization error: {e}")
        return None

    def set(self, key: str, value: Union[str, int, float], **kwargs) -> bool:
        """设置键值"""
        def _set():
            return self.redis_conn.set(name=key, value=value, **kwargs)
        return bool(self._retry_operation(_set))

    def get(self, key: str) -> Optional[str]:
        """获取键值"""
        return self._retry_operation(lambda: self.redis_conn.get(name=key))

    def delete(self, *keys: str) -> int:
        """删除键"""
        return self._retry_operation(lambda: self.redis_conn.delete(*keys)) or 0

    def exists(self, *keys: str) -> int:
        """检查键是否存在"""
        return self._retry_operation(lambda: self.redis_conn.exists(*keys)) or 0

    def expire(self, key: str, seconds: int) -> bool:
        """设置过期时间"""
        return bool(self._retry_operation(lambda: self.redis_conn.expire(key, seconds)))

    def ttl(self, key: str) -> int:
        """获取键的剩余生存时间"""
        return self._retry_operation(lambda: self.redis_conn.ttl(key)) or -2

    def keys(self, pattern: str = '*') -> List[str]:
        """获取匹配的键名列表"""
        return self._retry_operation(lambda: self.redis_conn.keys(pattern)) or []

    def flush_db(self) -> bool:
        """清空当前数据库"""
        return bool(self._retry_operation(lambda: self.redis_conn.flushdb()))

    def close(self) -> None:
        """关闭连接"""
        if self._redis_conn:
            self._redis_conn.close()
            self._connected = False

    def __enter__(self):
        """上下文管理器入口"""
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        """上下文管理器出口"""
        self.close()
